package com.dahua.messagesyncsh.service.Impl;

import com.alibaba.fastjson2.JSON;
import com.dahua.messagesyncsh.config.redis.RedisUtil;
import com.dahua.messagesyncsh.entity.Collection;
import com.dahua.messagesyncsh.entity.SensorData;
import com.dahua.messagesyncsh.kafka.enums.Fields;
import com.dahua.messagesyncsh.kafka.enums.Graph;
import com.dahua.messagesyncsh.pojo.*;
import com.dahua.messagesyncsh.rabbitmq.pojo.EqStatusMessage;
import com.dahua.messagesyncsh.service.CollectionService;
import com.dahua.messagesyncsh.service.EquipmentService;
import com.dahua.messagesyncsh.service.FaultService;
import com.dahua.messagesyncsh.service.ProcessorService;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.zip.GZIPOutputStream;

import static com.dahua.messagesyncsh.common.Constants.*;
import static com.dahua.messagesyncsh.pojo.Control56.convertToControl56;

@Slf4j
@Service
public class ProcessorServiceImpl implements ProcessorService {

    private static final List<String> excludedFieldListOfEquipmentStatus = Arrays.asList("ID", "MQ_Send_Status", "Server_Send_Status");


    @Resource
    private RedisUtil redisUtil;

    @Resource
    private CollectionService collectionService;

    @Resource
    private EquipmentService equipmentService;

    @Resource
    private FaultService faultService;

    //  暂时使用yml注入，new Runnable 会注入失败
    @Value("${consumer.url}")
    private String control56Url;

    @Value("${spring.redis.single-consumption}")
    private String consumption;

    @Value("${chart.path:/data/ftpdata}")
    private String chartPath;

    /*  图谱类型*/
    static HashMap<String, Graph> graphs = new HashMap<>();

    static HashMap<String, Fields> fields = new HashMap<>();

    static {
        /*  初始化graph类型*/
        for (Graph graph : Graph.values()) {
            graphs.put(graph.toString().toLowerCase(), graph);
        }

        for (Fields item : Fields.values()) {
            fields.put(item.toString(), item);
        }
    }

    /*  处理Feature数据*/
    @Override
    public SensorData buildData(Feature feature, EquipmentInfo enquire, Collection collection) {
        if (collection == null || collection.getSensorId() == null) {
            return null;
        }

        LocalDateTime dateTime = LocalDateTime.ofInstant(
                Instant.ofEpochMilli(feature.getStartTime()),
                ZoneId.systemDefault()
        );

        SensorData sensorData = SensorData.builder()
                .stationId(enquire.getStationId())
                .eqId(enquire.getId())
                .sensorId(collection.getSensorId())
                .vibSensorNo(collection.getSensorId())
                .time(dateTime.format(DateTimeFormatter.ofPattern(DATETIME_FORMATTER_ONE)))
                .build();


        /*  数值set入*/
        try {
            Gson gson = new Gson();
            Map<String, Double> map = gson.fromJson(JSON.toJSONString(feature.getData()), Map.class);

            for (String entry : map.keySet()) {
                Fields fields = ProcessorServiceImpl.fields.get(entry);
                if (fields != null) {
                    if (fields.getField().equals("ARMS")) {
                        sensorData.setArms(map.get(entry));
                    } else {
                        sensorData.setRms(map.get(entry));
                    }
                }
            }

            if (sensorData.getRms() == null && sensorData.getArms() == null) {
                log.error("【Feature】：data为空 \n {}", feature.getData());
                return null;
            }

        } catch (Exception e) {
            log.error("【Feature】：data转换失败 \n {}", e.getMessage());
            return null;
        }
        return sensorData;
    }

    /*  处理CMode数据*/
    @Override
    public SensorData buildData(CMode cMode, EquipmentInfo enquire, Collection collection) {
        if (collection == null || collection.getSensorId() == null) {
            return null;
        }

        LocalDateTime dateTime = LocalDateTime.ofInstant(
                Instant.ofEpochMilli(cMode.getStartTime()),
                ZoneId.systemDefault()
        );

        /*  存储*/
        SensorData sensorData = SensorData.builder()
                .stationId(enquire.getStationId())
                .eqId(enquire.getId())
                .sensorId(collection.getSensorId())
                .vibSensorNo(collection.getSensorId())
                .time(dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                .build();

        Graph graph = graphs.get(cMode.getType());
        if (graph == null) {
            log.error("【CMode】: graph类型不存在 \n {}", cMode.getType());
            return null;
        }

        String path = writeCMode(cMode, enquire, collection);
        if (path == null) {
            return null;
        }

        switch (graph) {
            case FFT:
                sensorData.setFftFile(path);
                break;
            case PSD:
                sensorData.setPsdFile(path);
                break;
            case ENVELOPE:
                sensorData.setEnvelopeFile(path);
                break;
            case CEP:
                sensorData.setCepFile(path);
                break;
            case OCTAVE:
                sensorData.setOctaveFile(path);
                break;
            case WAVEFORM:
            default:
                sensorData.setTimeSigFile(path);
                break;
        }
        return sensorData;
    }

    public String writeCMode(CMode cMode, EquipmentInfo info, Collection collection) {

        // 转换为Instant
        Instant instant = Instant.ofEpochMilli(cMode.getStartTime());

        // 设置时区
        ZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault());
        // 定义格式
        DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd");

        DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("HH");

        DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("HH-mm-ss");

        // 格式化输出
        String formatted1 = zonedDateTime.format(formatter1);
        String formatted2 = zonedDateTime.format(formatter2);
        String formatted3 = zonedDateTime.format(formatter3);

        if (collection == null) {
            return null;
        }

        String outputDir = chartPath + "/" + info.getId() + "/" + formatted1 + "/" + formatted2 + "/" + collection.getSensorId() + "/";

        String fileName = info.getId() + "_" + formatted1 + "-" + formatted3 + "-" + collection.getSensorId() + "_" + cMode.getType();

        String suffix1 = ".txt";

        String suffix2 = ".gz";


        Path path = Paths.get(outputDir + fileName + suffix1);

        try {
            // 创建父目录（如果不存在）
            if (path.getParent() != null) {
                Files.createDirectories(path.getParent());
            }

            // 写入原始文件（带缓冲）
            try (BufferedWriter writer = new BufferedWriter(
                    new OutputStreamWriter(
                            Files.newOutputStream(path), StandardCharsets.UTF_8))) {

                writer.write(collection.getSensorId() + " ");

                for (String line : cMode.getHeader()) {
                    writer.write(line);
                    writer.write(" ");
                }

                writer.newLine();

                for (String line : cMode.getData()) {
                    writer.write(line);
                    writer.newLine();
                }
            }
        } catch (Exception e) {
            log.error("【CMode】: 写入txt文件错误 \n {}", e.toString());
            return null;
        }

        Path sourcePath = Paths.get(outputDir + fileName + suffix1 + suffix2);

        try (InputStream input = Files.newInputStream(path);
             OutputStream output = Files.newOutputStream(sourcePath);
             GZIPOutputStream gzipOS = new GZIPOutputStream(output)) {

            byte[] buffer = new byte[1024];
            int len;
            while ((len = input.read(buffer)) != -1) {
                gzipOS.write(buffer, 0, len);
            }
        } catch (Exception e) {
            log.error("【CMode】: 写入gz文件错误 \n {}", e.toString());
            return null;
        }

        try {
            Files.deleteIfExists(path);
        } catch (Exception e) {
            log.error("【CMode】: 删除txt文件错误 \n {}", e.toString());
            return null;
        }

        return fileName + suffix1;
    }

    /*  处理485数据*/
    @Override
    public void handleT485(List<T485> t485s) {
        List<Control56> control56s = new ArrayList<>();
        for (T485 t485 : t485s) {
            try {
                EquipmentInfo enquire = equipmentService.enquire(t485.getEquipmentId());

                if (enquire == null) {
                    continue;
                }

                Control56 hash = null;

                /*  过滤当天相同数据*/
                try {
                    Object o = redisUtil.getHash(EQUIPMENT_T485_DATA, t485.getEquipmentId());
                    /*  数据未被置空*/
                    if (o != null && !o.equals(NULL)) {
                        hash = JSON.parseObject(JSON.toJSONString(o), Control56.class);
                    }
                } catch (Exception e) {
                    log.error("【Control56】：历史数据转换失败{}", e.getMessage());
                }

                if (hash != null && Control56.isEquals(hash, t485)) {
                    continue;
                }

                Control56 control56 = convertToControl56(t485, enquire);

                control56s.add(control56);

                //  当前最新状态数据
                redisUtil.setHash(EQUIPMENT_T485_DATA, t485.getEquipmentId(), control56);
                redisUtil.setHash(EQUIPMENT_485_IN_ + enquire.getBrandId(), t485.getEquipmentId(), NULL);

                log.info("【Control56】：{} 最新485数据更新 {}", control56.getEquipmentid(), t485.toString());

            } catch (Exception e) {
                log.error("【Control56】：数据处理失败{} \n {}", t485, e.getMessage());
            }
        }
        /*  处理完成后再上传*/
        if (!control56s.isEmpty()) {
            this.handleControl56(control56s);
            log.info("【Control56】：推送了{}条", control56s.size());
        }
    }

    @Override
    public T485 convert2T485(EqStatusMessage eqStatusMessage) {
        EquipmentInfo enquire = equipmentService.enquire(eqStatusMessage.getRegCod());
        if (enquire == null) {
            return null;
        }
        T485 t485 = T485.builder()
                .equipmentId(enquire.getRegisterNumber())
                .build();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime ldt = LocalDateTime.parse(eqStatusMessage.getTimeStamps(), formatter);
        long timestampMillis = ldt.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        t485.setDataTime(timestampMillis);
        t485.setManufacturer(enquire.getName());
        if (enquire.getBk2().equals("ZT")) {
            t485.setData(analysisZT(eqStatusMessage));
        } else {
            t485.setData(analysisFT(eqStatusMessage));
        }
        if (eqStatusMessage.getAlarmState() != null && eqStatusMessage.getAlarmState() == 1) {
            faultService.insert(eqStatusMessage, enquire);
        }
        return t485;
    }

    private List<Integer[]> analysisZT(EqStatusMessage eqStatusMessage) {
        List<Integer[]> list = new ArrayList<>();
        int code1 = 0;
        int code2 = 0; // 楼层

        // 当前服务模式
        if (eqStatusMessage.getServiceMode() != null) {
            switch (eqStatusMessage.getServiceMode()) {
                case 0:
                    code1 += 1; // 停止服务
                    break;
                case 1:
                    code1 += 2; // 正常运行
                    break;
                case 2:
                    code1 += 4; // 检修模式
                    break;
                case 3:
                    code1 += 8; // 其他
                    break;
            }
        }
        // 运行状态
        if (eqStatusMessage.getOperationStatus() != null) {
            switch (eqStatusMessage.getOperationStatus()) {
                case 0:
                    code1 += 16; // 停止
                    break;
                case 1:
                    code1 += 32; // 运行
                    break;
            }
        }
        // 运行方向
        if (eqStatusMessage.getOperationDirection() != null) {
            switch (eqStatusMessage.getOperationDirection()) {
                case 0:
                    code1 += 64; // 上行
                    break;
                case 1:
                    code1 += 128; // 下行
                    break;
            }
        }
        // 锁梯状态
        if (eqStatusMessage.getLockState() != null) {
            switch (eqStatusMessage.getLockState()) {
                case 0:
                    code1 += 256; // 正常
                    break;
                case 1:
                    code1 += 512; // 锁梯
                    break;
            }
        }
        // 关门到位信号
        if (eqStatusMessage.getDoorState() != null) {
            switch (eqStatusMessage.getDoorState()) {
                case 0:
                    code1 += 1024; // 未知
                    break;
                case 1:
                    code1 += 2048; // 关到位
                    break;
            }
        }
        // 故障信号
        if (eqStatusMessage.getFaultState() != null) {
            if (eqStatusMessage.getFaultState() == 1) {
                code1 += 4096; // 故障
            }
        }
        // 当前楼层
        if (eqStatusMessage.getCarPosition() != null) {
            code2 = Integer.parseInt(eqStatusMessage.getCarPosition());
        }

        Integer[] i1 = new Integer[]{40001, code1};
        Integer[] i2 = new Integer[]{40002, code2};
        list.add(i1);
        list.add(i2);
        return list;
    }

    private List<Integer[]> analysisFT(EqStatusMessage eqStatusMessage) {
        List<Integer[]> list = new ArrayList<>();
        int code1 = 0;
        // 当前服务模式
        if (eqStatusMessage.getServiceMode() != null) {
            switch (eqStatusMessage.getServiceMode()) {
                case 0:
                    code1 += 1; // 停止服务
                    break;
                case 1:
                    code1 += 2; // 正常运行
                    break;
                case 2:
                    code1 += 4; // 检修模式
                    break;
                case 3:
                    code1 += 8; // 其他
                    break;
            }
        }
        // 运行状态
        if (eqStatusMessage.getOperationStatus() != null) {
            switch (eqStatusMessage.getOperationStatus()) {
                case 0:
                    code1 += 16; // 停止
                    break;
                case 1:
                    code1 += 32; // 运行
                    break;
                case 2:
                    code1 += 64; // 急停
                    break;
            }
        }
        // 运行方向
        if (eqStatusMessage.getOperationDirection() != null) {
            switch (eqStatusMessage.getOperationDirection()) {
                case 0:
                    code1 += 128; // 停止
                    break;
                case 1:
                    code1 += 256; // 上行
                    break;
                case 2:
                    code1 += 512; // 下行
                    break;
            }
        }
        // 故障信号
        if (eqStatusMessage.getFaultState() != null) {
            if (eqStatusMessage.getFaultState() == 1) {
                code1 += 1024; // 故障
            }
        }
        Integer[] i = new Integer[]{40001, code1};
        list.add(i);
        return list;
    }

    public void handleControl56(List<Control56> control56s) {
        try {
            List<Map<?, ?>> dataList = new ArrayList<>(control56s.size());

            for (Control56 control56 : control56s) {
                Map<?, ?> eqStatusMap = buildMap(control56, excludedFieldListOfEquipmentStatus);

                dataList.add(eqStatusMap);
            }
            String dataListJson = new Gson().toJson(dataList);
            Map<String, String> jsonMap = Collections.singletonMap("control56Data", dataListJson);
            String json = new Gson().toJson(jsonMap);
            byte[] sendData = compress(json);

            SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
            //  设置超时
            requestFactory.setConnectTimeout(30000);
            requestFactory.setReadTimeout(600000);
            RestTemplate restTemplate = new RestTemplate(requestFactory);
            //  不用增加配置

            try {
                Map<?, ?> response = restTemplate.postForObject(control56Url, sendData, Map.class);
            } catch (Exception e) {
                log.error("【485数据】：传输失败 {}  {}", e.getMessage(), control56s);
                for (Control56 control56 : control56s) {
                    redisUtil.leftPush(CONTROL56_LIST, control56);
                }
            }
        } catch (Exception e) {
            log.error("【485数据】：其他错误 {}", e.getMessage());
        }

    }


    public static byte[] compress(String str) throws IOException {
        if (null == str || str.isEmpty()) {
            return null;
        }
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        OutputStream outStream = new GZIPOutputStream(stream);
        outStream.write(str.getBytes(StandardCharsets.UTF_8));
        outStream.close();
        return stream.toByteArray();
    }


    private Map<?, ?> buildMap(Object object, List<String> excludedFieldList) throws IllegalAccessException {
        if (object == null) {
            return null;
        }
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        Map<String, Object> map = new HashMap<>();
        Field[] fields = object.getClass().getDeclaredFields();
        if (fields.length < 1) {
            return null;
        }
        for (Field field : fields) {
            String name = field.getName();
            if (excludedFieldList != null && excludedFieldList.contains(name)) {
                continue;
            }
            if (Modifier.isStatic(field.getModifiers())) { //静态成员不用上传
                continue;
            }
            field.setAccessible(true);
            if (field.getType() == Date.class) {
                map.put(name, simpleDateFormat.format(field.get(object)));
            } else {
                map.put(name, field.get(object));
            }
        }
        return map;
    }

    private List<Map<?, ?>> buildList(List<Object> objectList, List<String> excludedFieldList) throws IllegalAccessException {
        if (objectList == null || objectList.isEmpty()) {
            return null;
        }
        List<Map<?, ?>> mapList = new ArrayList<>(objectList.size());
        for (Object object : objectList) {
            mapList.add(buildMap(object, excludedFieldList));
        }
        return mapList;
    }
}
